# -*- coding: utf-8 -*-
#
# Sorto, the sorting game
# Copyright (C) 2012 Ralph Preston
# ralph.preston@gmail.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to:
#
# Free Software Foundation, Inc.
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301
# USA.
#
import os
import sys
import copy
import time
import math
import pprint
import socket
import datetime
import threading
import Queue

from PyQt4 import QtCore, QtGui
from sorto_gui          import Ui_MainWindow
from sorto_controller   import SortoController
from sorto_board        import SortoBoard
from sorto_base_view    import SortoBaseView
from sorto_record       import SortoRecord
from sorto_start_server import StartServerDialog
from sorto_join_remote_server import JoinRemoteServerDialog
from sorto_counter      import SortoCounter

class StartQT4(QtGui.QMainWindow, SortoBaseView):

    # Remove this when releasing. This is to automatically load a
    # SortoPlayer at startup. Saves the hassle of having to "Load Player"
    # after every startup.
    DEBUG_LOAD_FILE = 'sorto_player.py'

    MAX_PLAYERS = 6

    NUMBER_OF_SUMS = 16

    # Turn actions
    MOVING_COUNTER    = 'moving counter'
    ANALYZING_MOVE    = 'analyzing'

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        SortoBaseView.__init__(self, max_games=1, logfile='sorto_game.log')
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self._abort = False
        self._simulation_complete_timer = QtCore.QTimer(self)

        # Connect signals with slots
        QtCore.QObject.connect(self.ui.button_start, 
                               QtCore.SIGNAL("clicked()"),
                               self.clicked_start)
        QtCore.QObject.connect(self.ui.button_stop, 
                               QtCore.SIGNAL("clicked()"),
                               self.clicked_stop)
        QtCore.QObject.connect(self._simulation_complete_timer,
                               QtCore.SIGNAL("timeout()"),
                               self._check_for_simulation_complete)
        QtCore.QObject.connect(self.ui.toolButton, 
                               QtCore.SIGNAL("clicked()"),
                               self.lineEdit_set_logfile)
        
        self._player_counters_lock = threading.Lock()

        # Setup a Queue for communicating with the controller
        # thread. Used for clicking on counters and moving them.
        self._selected_counter_queue        = Queue.Queue(maxsize=1)
        self._selected_counter_placed_queue = Queue.Queue(maxsize=1)
        self._simulation_complete           = Queue.Queue(maxsize=1)
        self._turn_action_queue             = Queue.Queue()
        self._new_text                      = Queue.Queue()
        self._update_player_list_queue      = Queue.Queue()
        self._initialize_moving_object_data()

        self._state = None
        self._game_stopped = True
        self._active_player = None
        self._last_game_painted = None
        self._controller_running = False
        self._remote_server_started = False
        self._remote_server_ip      = None
        self._remote_server_port    = None

        # Determine the values to use to adjust for the menubar
        self._x_offset = 0
        self._y_offset = 0
        self._menu_height = 0
        if sys.platform != 'darwin':
            # In windows the counters need to be moved down by the
            # title bar height
            self._x_offset = 1
            self._y_offset = 1
            if sys.platform == 'linux2':
                self._x_offset = 0
                self._y_offset = 0
                pass
            self._menu_height = self.ui.menubar.height()
            pass
        
        # Start server dialog box
        self._server_dialog = None

        # Join remote game dialog box
        self._join_remote_dialog = None

        # Record the starting position of the counters
        self._counter_1_pos = QtCore.QPoint(210, 30)
        self._counter_2_pos = QtCore.QPoint(290, 30)
        self._counter_3_pos = QtCore.QPoint(370, 30)

        self._player_counter_colors = {}
        self._counters = []
        for ii in range(1,13):
            c = SortoCounter(parent=self, number=ii, color='#ff0000', size=64)
            c.raise_()
            self._counters.append(c)
            pass

        # Reset the board to starting values
        for space in range(36):
            counter_attr = 'label_%d' % (space)
            obj = getattr(self.ui, counter_attr)
            if space < 3:
                index = space + 1
            elif space < 35:
                _tmp = space / 2
                index = _tmp + 2
            else:
                index = 20
                pass
            pixmapname = './images/%d.tif' % (index)
            obj.setPixmap(QtGui.QPixmap(pixmapname))
            pass
        
        # Initialize the rounds required to win information
        self._label_rnds = [
            RoundsRequiredToWin(0,2),
            RoundsRequiredToWin(3,4),
            RoundsRequiredToWin(5,6),
            RoundsRequiredToWin(7,8),
            RoundsRequiredToWin(9,10),
            RoundsRequiredToWin(11,12),
            RoundsRequiredToWin(13,14),
            RoundsRequiredToWin(15,16),
            RoundsRequiredToWin(17,19),
            RoundsRequiredToWin(20,23),
            RoundsRequiredToWin(24,28),
            RoundsRequiredToWin(29,34),
            RoundsRequiredToWin(35,41),
            RoundsRequiredToWin(42,49),
            RoundsRequiredToWin(50,58),
            RoundsRequiredToWin(59,self.controller.MAX_ROUNDS),
            ]


        # Set the logging checkbox
        self.ui.checkBox_log.setChecked(True)
        self.ui.lineEdit_logfile.setText(self.logfile)

        # Load the sorto players
        self._initialize_player_information()
        self._initialize_game_information()

        if self.DEBUG_LOAD_FILE:
            self.on_actionLoad_Player_triggered(False)
            pass
        return

    def abort(self):
        self._game_stopped = True
        self._abort = True
        
        if self.controller:
            self.controller.abort()
            if self._controller_running:
                self.controller.join()
                pass
            self._controller_running = False
            self.controller = None
            pass
        return

    def closeEvent(self, event):
        self.controller.stop_remote_server()
        self.abort()
        return

    def paintEvent(self, event):
        """
        Called when a refresh event arrives
        """
        text = ''
        while not self._new_text.empty():
            line = self._new_text.get()
            if text:
                text = '%s\n%s' % (text, line)
            else:
                text = line
                pass
            pass
        if text:
            self.ui.text_status_log.append(text)
            pass

        # If the most recent more is being analyzed don't change the view
        if self._state == self.MOVING_COUNTER: return

        if not self._update_player_list_queue.empty():
            (player_list, current_index) = self._update_player_list_queue.get()
            self.ui.combo_player_to_view.clear()
            self.ui.combo_player_to_view.addItems(player_list)
            self.ui.combo_player_to_view.setCurrentIndex(current_index)
            pass

        self._active_player = str(self.ui.combo_player_to_view.currentText())
        self.ui.label_roll_value.setText('%d' % self._die_roll)
        self.ui.label_statistics.setText('Statistics for %s' % self._active_player)

        # Update the number of games played
        self.ui.label_games_played.setText('Games played: %d' % \
                                               (self._game_number+1))

        # Get the counter color for this player
        self._player_counters_lock.acquire()
        if self._active_player in self._player_counter_colors:
            counter_color = self._player_counter_colors[self._active_player]
        else:
            counter_color = None
            pass
        
        # Update the number of games won in each round group.
        records = self._player_records
        if self._active_player in records:
            rec = records[self._active_player]
            total_score = rec.get_total_score()
            average_rounds_per_game = rec.get_average_rounds_per_game()
            self.ui.label_point_total.setText('Point total: %d' % total_score)
            self.ui.label_round_average.setText('Round average: %f' % average_rounds_per_game)
            pass
        self._player_counters_lock.release()
        
        # Check if we need to undo the last move
        self._callback_lock.acquire()
        if self._undo_moving_object == True:
            self._moving_object.move(self._moving_object_starting_pos)
            self._moving_object = None
            self._undo_moving_object = False
            pass
        self._callback_lock.release()

        # Setup the player's board. Turn off all counters that aren't
        # on the board. After this make visible the counters in play
        # and at the top of the screen.
        for obj in self._counters:
            obj.setVisible(False)
            pass
        
        board = self._player_boards.get(self._active_player, [])
        self._player_counters_lock.acquire()
        try:
            # Set the Rounds required to win information
            if self._active_player in records:
                games_won_list = [0] * self.controller.MAX_ROUNDS
                for round in range(self.controller.MAX_ROUNDS):
                    games_won_list[round] = records[self._active_player].get_games_won_on_round(round)
                    pass
                for ii in range(len(self._label_rnds)):
                    label_str = 'label_rnds_%d' % ii
                    obj = getattr(self.ui, label_str)
                    label_data = self._label_rnds[ii]
                    min = label_data.min
                    max = label_data.max
                    obj.setText('%d-%d:\t%d' % (min, max,
                                                sum(games_won_list[min:max+1])))
                    pass
                self._last_game_painted = self._game_number
                pass

            for index in range(len(board)):
                counter = board[index]
                if counter == SortoBoard.EMPTY:
                    continue
                obj = self._counters[counter-1]
                loc = self._player_counter_positions[self._active_player].get(index, None)
                if loc:
                    obj.move(loc)
                    pass
                if counter_color:
                    obj.color = counter_color
                    obj.setVisible(True)
                    pass
                pass
            
            # Redraw the player's counters at the top of the screen
            for index in range(0, 3):
                # Draw the counters for the active player
                if self._active_player not in self._player_counters:
                    break
                # Get the counter to draw
                counter = None
                if index < len(self._player_counters[self._active_player]):
                    counter = self._player_counters[self._active_player][index]
                    pass
                if counter is None:
                    continue
                # Get the counter label, make it visible
                obj = self._counters[counter-1]
                #if self._state is None:
                counter_location = '_counter_%d_pos' % (index+1)
                counter_location_obj = getattr(self, counter_location)
                obj.move(counter_location_obj)
                pass
                if counter_color:
                    obj.color = counter_color
                    obj.setVisible(True)
                    pass
                pass
        finally:
            self._player_counters_lock.release()
            pass

        return

    def mousePressEvent(self, event):
        pos = event.pos()
        x = pos.x()
        y = pos.y()
 
        # If the mousePress happened in any of the counters move them.
        if not self._turn_action_queue.empty():
            self._state = self._turn_action_queue.get()
        elif self._state is None:
            return

        # ToDo: Try using main window childAt(pos)
        # Check if the mousePress happened in any of the off board
        # counters and determine which one it was.
        counter = self._find_moving_counter(x,y)
        if counter:
            self._selected_counter_queue.put(counter)
            if self._state is not self.ANALYZING_MOVE:
                self._callback_lock.acquire()
                self._moving_object_starting_pos = self._moving_object.pos()
                self._callback_lock.release()
                pass
            self._state = self.MOVING_COUNTER
            pass

        return

    def mouseMoveEvent(self, event):
        self._callback_lock.acquire()
        try:
            if self._moving_object is None: return
            pos = event.pos()
            x = pos.x()
            y = pos.y()
            half_width  = self._moving_object.width()/2
            half_height = self._moving_object.height()/2
            new_x = x - self._moving_object_click_point_x - half_width
            new_y = y - self._moving_object_click_point_y - half_height
            self._moving_object.move(new_x, new_y)
            for obj in self._counters:
                if obj.isVisible():
                    obj.repaint()
                    pass
                pass
        finally:
            self._callback_lock.release()
            pass
        return

    def mouseReleaseEvent(self, event):
        if self._state is not self.MOVING_COUNTER: return
        pos = event.pos()
        x = pos.x() + self._x_offset
        y = pos.y() - (self._menu_height - self._y_offset)
        # Determine the board location for this coordinate.  Put the
        # index corresponding to the space into the queue. Put None if
        # the user didn't place the counter on the board.
        location_placed = None
        self._player_counters_lock.acquire()
        for ii in range(36):
            label_str = 'label_%d' % ii
            obj = getattr(self.ui, label_str)

            if self._is_point_in_circular_widget(x,y,obj):
                location_placed = ii
                self._active_player = str(self.ui.combo_player_to_view.currentText())
                if self._active_player not in self._player_counter_positions:
                    self._player_counter_positions[self._active_player] = {}
                    pass
                self._player_counter_positions[self._active_player][ii] = obj.pos()
                break
            pass
        self._player_counters_lock.release()
        self._selected_counter_placed_queue.put(location_placed)
        self._state = self.ANALYZING_MOVE
        self.repaint()
        return

    def stateChanged_log_checkbox(self, val):
        if val == QtCore.Qt.Unchecked:
            self.ui.lineEdit_logfile.setVisible(False)
            self.ui.toolButton.setVisible(False)
            self.logfile = None
        else:
            self.ui.lineEdit_logfile.setVisible(True)
            self.ui.toolButton.setVisible(True)
            pass
        self.controller.set_logfile(self.logfile)
        return

    def lineEdit_set_logfile(self):
        fd = QtGui.QFileDialog(self, "Set logfile...", '.', ('*.log'))
        filename = fd.getSaveFileName()
        self.logfile = filename
        self.ui.lineEdit_logfile.setText(self.logfile)
        self.controller.set_logfile(self.logfile)
        return

    def clicked_start(self):
        self._startup_gui()
        self._update_player_choice()
        self._controller_running = True
        self.controller.start()
        return

    def clicked_stop(self):
        self._game_stopped = True
        self._simulation_complete_timer.stop()
        self._new_text.put("Stopping simulation")
        self._new_text.put("End time: %s" % str(datetime.datetime.now()))
        self.ui.button_stop.setEnabled(False)
        self.controller.abort()
        if self._controller_running:
            self.controller.join()
            self._controller_running = False
            pass

        self.ui.button_start.setEnabled(True)
        self.ui.checkBox_log.setEnabled(True)
        self.ui.lineEdit_logfile.setEnabled(True)
        self.ui.toolButton.setEnabled(True)
        # Create a new SortoController because threads cannot be
        # restarted, according to RuntimeError.
        self.controller = SortoController(view=self, logfile=self.logfile,
                                          start_remote_server=self._remote_server_started, ip=self._remote_server_ip, port=self._remote_server_port)
        for player in self.player_files:
            self.controller.load_player(self.player_files[player], quiet=True)
            pass
        return

    def _startup_gui(self):
        self._game_stopped = False
        if self.ui.lineEdit_logfile.isVisible():
            self.logfile = str(self.ui.lineEdit_logfile.displayText())
            pass

        while not self._simulation_complete.empty():
            res = self._simulation_complete.get()
            pass

        self.ui.button_start.setEnabled(False)
        self.ui.button_stop.setEnabled(True)
        self.ui.checkBox_log.setEnabled(False)
        self.ui.lineEdit_logfile.setEnabled(False)
        self.ui.toolButton.setEnabled(False)
        self.max_games = self.ui.spinBox_rounds.value()
        self._new_text.put("\nSimulating %d games" % self.max_games)
        self._new_text.put("Start time: %s" % str(datetime.datetime.now()))
        self._initialize_game_information()
        self._simulation_complete_timer.start(500)

        self.controller.set_logfile(self.logfile)
        return

    #####################################################################
    #
    # Callbacks from sorto_real_player. Needed to allow for player
    # actions.
    #
    def display_text(self, text):
        self._new_text.put(text)
        return

    def accept_place_counter(self):
        self._initialize_moving_object_data()
        return

    def undo_place_counter(self):
        if not self._game_stopped:
            self._new_text.put("Illegal move.")
            pass
        self._callback_lock.acquire()
        if self._moving_object:
            self._undo_moving_object = True
            pass
        self._callback_lock.release()
        return

    def place_counter(self, player_name):
        location_index = None
        while not self._game_stopped:
            try:
                location_index = self._selected_counter_placed_queue.get(timeout=0.1)
                break
            except Queue.Empty:
                continue
            pass
        return location_index    
    #
    #
    #####################################################################

    def on_actionLoad_Player_triggered(self, checked=None):
        if checked is None: return
        if self._player_count >= self.MAX_PLAYERS: return

        if self.DEBUG_LOAD_FILE:
            filename = self.DEBUG_LOAD_FILE
            self.DEBUG_LOAD_FILE = None
        else:
            fd = QtGui.QFileDialog(self, "Load player", '.', ('*.py'))
            filename = fd.getOpenFileName()
            pass

        if not filename: return

        player_name = self.controller.load_player(filename)
        self._active_player = player_name
        self.player_files[player_name] = filename
        # default to red counters
        self._add_player_to_choice(player_name)
        return

    def _add_player_to_choice(self, player_name):
        self._player_index[player_name] = self._player_count
        self._player_count += 1

        player_list = list(self.player_files.keys()) + \
            list(self.remote_player_files.keys())
        player_list.sort()
        if len(player_list) == 0:
            return
        current_index = player_list.index(player_name)
        self._update_player_list_queue.put((player_list, current_index))
        return
    
    def _update_player_choice(self):
        player_list = self._player_boards.keys()
        player_list.sort()
        if len(player_list) == 0:
            return
        self._update_player_list_queue.put((player_list, 0))
        return

    def on_actionJoin_Remote_Game_triggered(self, checked=None):
        if checked is None: return

        if not self._join_remote_dialog:
            self._join_remote_dialog = JoinRemoteServerDialog(self)
            pass
        self._join_remote_dialog.show()
        return

    def on_actionClear_Players_triggered(self, checked=None):
        if checked is None: return

        if not self._game_stopped: return

        self.controller.clear_players()

        self.ui.combo_player_to_view.clear()

        self._initialize_player_information()
        return

    def on_actionStart_server_triggered(self, checked=None):
        if checked is None: return
        if not self._server_dialog:
            self._server_dialog = StartServerDialog(self)
            pass
        self._server_dialog.show()
        return

    def startServerDialog_ok_clicked(self, ip, port):
        self._server_dialog.hide()

        try:
            port = int(port)
            self.controller.start_remote_server(ip, port)
            self._remote_server_started = True
            self._remote_server_ip      = ip
            self._remote_server_port    = port
        except Exception:
            self.display_text("Failed to start remote server on %s:%s" % (str(ip), str(port)))
            self.ui.actionStart_server.setChecked(False)
            pass
        return

    def startServerDialog_cancel_clicked(self, text=''):
        self._server_dialog.hide()
        self.ui.actionStart_server.setChecked(False)
        self._remote_server_started = False
        if text:
            self.display_text(text)
            pass
        return

    def joinRemoteDialog_ok_clicked(self, player_file, ip, port):
        self._join_remote_dialog.hide()

        try:
            port = int(port)
        except Exception:
            self.display_text("%s failed to join game on the remote server at %s:%s" % (player_file, str(ip), str(port)))
            pass
        server_address = str(ip) + ":" + str(port)
        player_name = self.controller.load_remote_player(player_file, 
                                                         server_address)
        if player_name is None:
            self.display_text("Failed to join remote server at %s" % server_address)
            return
        self.remote_player_files[player_name] = (player_file, server_address)
        self._add_player_to_choice(player_name)
        self._startup_gui()
        return

    def add_remote_player(self, player_name, player_file, server_address):
        self.remote_player_files[player_name] = (player_file, server_address)
        self._add_player_to_choice(player_name)
        self._player_records[player_name] = SortoRecord()
        return

    def remove_remote_player(self, player_name, player_file):
        if player_name in self.remote_player_files:
            del self.remote_player_files[player_name]
            pass

        if player_name in self._player_records:
            del self._player_records[player_name]
            pass
        
        self._update_player_choice()
        
        return

    def joinRemoteDialog_cancel_clicked(self, text=''):
        self._join_remote_dialog.hide()
        if text:
            self.display_text(text)
            pass
        return

    def set_simulation_complete(self):
        # This is a callback from the controller and it is impossible
        # to send Qt events from a non-QtThread. The best I can think
        # of is to set a flag and use a timer in the GUI thread to
        # periodically check it. The alternative is to make the
        # controller a QtThread but that would require the console to
        # import Qt or complicate inheritance.
        self._simulation_complete.put(True)
        return

    def set_player_color(self, player_name, color):
        self._player_counters_lock.acquire()
        self._player_counter_colors[player_name] = color
        self._player_counters_lock.release()        
        return

    def set_die_roll(self, roll):
        self._die_roll = roll
        return

    def set_board_state(self, player, player_counters, board, counters):
        self._player_counters_lock.acquire()
        SortoBaseView.set_board_state(self, player, player_counters, 
                                      board, counters)
        self._state = None
        # Move the counters to their played locations
        for index in range(len(board)):
            counter_at_index = board[index]
            if counter_at_index == SortoBoard.EMPTY: continue

            # Figure out the coordinates for this index:
            label_str = 'label_%d' % index
            label = getattr(self.ui, label_str)
            if player not in self._player_counter_positions:
                self._player_counter_positions[player] = {}
                pass
            pos = label.pos()
            if sys.platform != 'darwin':
                y = pos.y() + self._menu_height - self._y_offset
                x = pos.x() - self._x_offset
                pos.setY(y)
                pos.setX(x)
                pass
            self._player_counter_positions[player][index] = pos
            pass
        self._player_counters_lock.release()

        return
    
    def set_points_won(self, player_name, points):
        self._player_counters_lock.acquire()
        if player_name not in self._player_records:
            self._player_records[player_name] = SortoRecord()
            pass
        rec = self._player_records[player_name]
        rec.record_score(self._game_number, self._round, points)
        rec.update_statistics()
        self._player_counters_lock.release()
        return

    def _check_for_simulation_complete(self):
        if self._simulation_complete.empty():
            return
        res = self._simulation_complete.get()
        if res:
            self._simulation_complete_timer.stop()
            self.clicked_stop()
            pass
        return

    def _initialize_player_information(self):
        self.player_files     = {} # map of player name to file
        self._player_boards    = {} # map player name to a history of boards
        self.remote_player_files = {}
        self._player_count     = 0
        self._player_index     = {} # map player name to their index

        self._player_counters_lock.acquire()
        self._player_counters  = {} # map player name to a history of counters

        # map player name to their counter positions
        self._player_counter_positions = {} 
        self._player_counters_lock.release()

        self._update_player_choice()
        return

    def _initialize_game_information(self):
        self._last_game_painted = None
        self._player_boards    = {} # map player name to a history of boards
        self._game_number = 0
        # Reset player's scores to zero
        self._player_records = {} # map player names to their score
        for player_name in self.player_files:
            self._player_records[player_name] = SortoRecord()
            self._player_boards[player_name] = SortoBoard()
            pass
        
        for player_name in self.remote_player_files.keys():
            self._player_records[player_name] = SortoRecord()
            self._player_boards[player_name] = SortoBoard()
            pass

        self._die_roll = 20

        # Move the counters into location and hide them
        for obj in self._counters:
            obj.move(self._counter_1_pos)
            obj.setVisible(False)
            pass

        return

    def display_board_for_player(self, player_name):
        player_index = self._player_index[player_name]
        self.ui.combo_player_to_view.setCurrentIndex(player_index)
        return

    def get_counter_from(self, location):
        self._turn_action_queue.put(location)
        counter = None
        while not self._game_stopped:
            try:
                counter = self._selected_counter_queue.get(timeout=0.1)
                break
            except Queue.Empty:
                continue
            pass
        return counter

    def _find_moving_counter(self, x, y):
        counter = None
        for index in range(1, 13):
            obj = self._counters[index-1]
            if not obj.isVisible():
                continue
            
            if self._is_point_in_circular_widget(x,y,obj):
                if self._state == self.OFF_BOARD_COUNTER:
                    if obj.pos() != self._counter_1_pos and \
                            obj.pos() != self._counter_2_pos and \
                            obj.pos() != self._counter_3_pos:
                        return None
                    pass

                obj.raise_()
                counter = index
                (center_x, center_y) = self._get_center_of_widget(obj)
                click_x = x-center_x
                click_y = y-center_y
                self._callback_lock.acquire()
                self._moving_object = obj
                self._moving_object_click_point_x = click_x
                self._moving_object_click_point_y = click_y
                self._callback_lock.release()
                break
            pass
        return counter

    def _get_center_of_widget(self, widget):
        widget_pos = widget.pos()
        widget_rect = widget.rect()
        center_x = widget_pos.x() + (widget_rect.width()/2)
        center_y = widget_pos.y() + (widget_rect.height()/2)
        return (center_x, center_y)

    def _is_point_in_circular_widget(self, x, y, widget):
        (center_x, center_y) = self._get_center_of_widget(widget)
        
        click_x = x-center_x
        click_y = y-center_y
        radius  = widget.height()/2
        if ((click_x*click_x) + (click_y*click_y)) < (radius*radius):
            return True
        return False

    def _initialize_moving_object_data(self):
        self._callback_lock.acquire()
        self._moving_object = None
        self._moving_object_click_point_x = 0
        self._moving_object_click_point_y = 0
        self._moving_object_starting_pos  = None
        self._undo_moving_object = False
        self._callback_lock.release()
        return

    pass # End of class StartQT4


class RoundsRequiredToWin(object):
    def __init__(self, min, max):
        self.min = min
        self.max = max
        return
    pass # End of class RoundsRequiredToWin


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())
    pass

